#pragma once

#include"Common.h"

#include"Location.h"
#include"Command.h"

namespace Custom
{
	class Unit
	{
	public:
		int Hp;
		Location Position;
		int AttackCoolDown;
		bool IsAttackFrame;
		BWAPI::UnitType Type;
		BWAPI::Unit* RealUnitPointer;
		Command CurrentCommand;
		bool IsIdle;
		bool NeedToCheckNearUnits;
		float FacingAngle;
		//--
		Unit(BWAPI::Unit* unit)
			:Hp(unit->getHitPoints() + unit->getShields()),
			Position(unit->getPosition().x() , unit->getPosition().y()),
			AttackCoolDown(unit->getGroundWeaponCooldown()),
			IsAttackFrame(unit->isAttackFrame()),
			Type(unit->getType()),
			RealUnitPointer(unit),
			arrivalCountDown(0),
			IsIdle(true),
			NeedToCheckNearUnits(false),
			FacingAngle(unit->getAngle())
		{
		}
		void Tick()
		{
			if(Alive())
			{
				--AttackCoolDown;
				if(IsAttackFrame)
				{
					if(Type.groundWeapon().damageCooldown() - AttackCoolDown > 10)
					{
						IsAttackFrame = false;
					}
				}
				else
				{
					if(CurrentCommand.Type == Command::Move)
					{
						Move(CurrentCommand.TargetPosition);
					}
					else if(CurrentCommand.Type == Command::Attack)
					{
						Attack(CurrentCommand.TargetUnit);
					}
					else if(CurrentCommand.Type == Command::DoNothing)
					{
						IsIdle = true;
					}
				}
			}
		}
		void Attack(Custom::Unit* target)
		{
			if(Alive())
			{
				if(!target->Alive())
				{
					CurrentCommand.Type = Command::DoNothing;
					IsIdle = true;
				}
				else
				{
					if(TurnTowardAngle(FacingAngle , Position.AngleToTarget(target->Position) , M_PI_4 - 0.15f))
					{
						if(GetEdgeDistange(*target) < Type.groundWeapon().maxRange())
						{
							if(AttackCoolDown <= 0)
							{
								target->Hp -= Type.groundWeapon().damageAmount() * Type.maxGroundHits();
								if(target->Hp < 0)
								{
									target->Hp = 0;
								}
								AttackCoolDown = Type.groundWeapon().damageCooldown();
								IsAttackFrame = true;
							}
						}
						else
						{
							NeedToCheckNearUnits = true;
							Position.MoveTowardLocation(target->Position , Type.topSpeed());
						}
					}
				}
			}
		}
		void Move(const Location& targetPosition)
		{
			if(Alive())
			{
				if(TurnTowardAngle(FacingAngle , Position.AngleToTarget(targetPosition) , M_PI_4))
				{
					Position.MoveTowardLocation(targetPosition , Type.topSpeed());
					--arrivalCountDown;
					if(arrivalCountDown <= 0)
					{
						CurrentCommand.Type = Command::DoNothing;
						IsIdle = true;
					}
				}
			}
		}
		void IssueCommand(const Command& command)
		{
			if(Alive())
			{
				CurrentCommand = command;
				IsIdle = false;
				if(command.Type == Command::Move)
				{
					arrivalCountDown = command.TargetPosition.GetDistance(Position) / Type.topSpeed();
				}
			}
		}
		float GetX() const
		{
			return Position.X;
		}
		float GetY() const
		{
			return Position.Y;
		}
		bool Alive() const
		{
			return Hp > 0;
		}
		float GetEdgeDistange(const Custom::Unit& anotherUnit) const
		{
			float dx = 0.0;
			float dy = 0.0;
			const float top = Position.Y - Type.dimensionUp();
			const float bottom = Position.Y + Type.dimensionDown();
			const float anotherTop = anotherUnit.Position.Y - anotherUnit.Type.dimensionUp();
			const float anotherBottom = anotherUnit.Position.Y + anotherUnit.Type.dimensionDown();
			dy = top - anotherBottom;
			if(dy < 0.0)
			{
				dy = anotherTop - bottom;
				if(dy < 0.0)
				{
					dy = 0.0;
				}
			}

			const float left = Position.X - Type.dimensionLeft();
			const float right = Position.X + Type.dimensionRight();
			const float anotherLeft = anotherUnit.Position.X - anotherUnit.Type.dimensionLeft();
			const float anotherRight = anotherUnit.Position.X + anotherUnit.Type.dimensionRight();
			dx = left - anotherRight;
			if(dx < 0.0)
			{
				dx = anotherLeft - right;
				if(dx < 0.0)
				{
					dx = 0.0;
				}
			}

			return sqrt(dx * dx + dy * dy);
		}
	private:
		bool TooFar(const Custom::Unit& target) const
		{
			return (Position.GetDistance(target.Position) > Type.groundWeapon().maxRange() + 50);
		}
		int arrivalCountDown;
	};
}